/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package modele;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.sql.Array;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import org.jdom.JDOMException;
import org.joda.time.DateTime;
import org.jopendocument.dom.template.JavaScriptFileTemplate;
import org.jopendocument.dom.template.TemplateException;

/**
 *
 * @author Simon Le modèle exécute les instructions et toutes les opérations
 * associèes.
 */
public class Modele extends Observable {
//  Le modele est observé par l'interface pour que cette dernière affiche le résultat des opérations.

    /**
     *
     * @param bonEnSaisie
     */
    public void enregistrerBdc(BonEnSaisie bonEnSaisie) {

        this.setChanged();
        Connection connexion;
        int idPrestataire;
        int idIntervenant;
        CriteresRecherche criteres = null;
//        Signale à l'interface quelle doit se préparer à un changement.
        try {
            Class.forName("org.postgresql.Driver");
//          Chargement du driver JDBC spécifique aux bases de données PostGreSQL.
            connexion = DriverManager.getConnection("jdbc:postgresql://10.0.1.65:5432/gbdc", "bdcgen", "mdp");
//          Etablit la connection à la BD à l'adresse indiquée avec identifiant et mot de passe.
            connexion.setAutoCommit(false);
            try {
            idPrestataire = this.chercherPrestataire(bonEnSaisie, connexion);
            if (idPrestataire == -1) {
                idPrestataire = this.creerPrestataire(bonEnSaisie, connexion);
            }

            idIntervenant = this.chercherIntervenant(bonEnSaisie, connexion);
            if (idIntervenant == -1) {
                idIntervenant = this.creerIntervenant(bonEnSaisie, connexion);
            }

            criteres = creerBonDeCommande(bonEnSaisie, idPrestataire, idIntervenant, connexion);
            
            connexion.commit();
            } catch (Exception e){
                connexion.rollback();
                this.notifyObservers(e);
            }
        } catch (SQLException | ClassNotFoundException e) {
            this.notifyObservers(e);
        }
        this.rechercherBdc(criteres);
    }

    public void rechercherBdc(CriteresRecherche criteres) {

        this.setChanged();
        ArrayList resultatsRecherche = new ArrayList();
        Connection connexion;
        PreparedStatement pstmt;
        ResultSet resultats;
        int nb = 0;
        //début modif lucile
        int blocnO = 0;
        int blocS = 0;
        int blocSess = 0;
        int blocSite = 0;
        int blocAn = 0;
        int blocM = 0;
        int blocN = 0;

        try {
            Class.forName("org.postgresql.Driver");
            connexion = DriverManager.getConnection("jdbc:postgresql://10.0.1.65:5432/gbdc", "bdcgen", "mdp");
            String requete = "SELECT * FROM bon_de_commande WHERE";

            if (criteres.getNomIntervenant() != null) {
                requete += " id_intervenant IN (SELECT id_intervenant FROM intervenants WHERE nom = ?)";
                nb++;
                blocnO = nb;
            }
            if (criteres.getSemaine() != 0) {
                if (nb > 0) {
                    requete += " AND";
                }
                requete += " EXTRACT (WEEK FROM date[1])=?";
                nb++;
                blocS = nb;
            }
            if (criteres.getSession() != null) {
                if (nb > 0) {
                    requete += " AND";
                }
                requete += " session = ?";
                nb++;
                blocSess = nb;
            }
            if (criteres.getSite() != 0) {
                if (nb > 0) {

                    requete += " AND";
                }
                requete += " site = ?";
                nb++;
                blocSite = nb;
            }
            if (criteres.getAnnee() != 0) {
                if (nb > 0) {
                    requete += " AND";
                }
                requete += " EXTRACT (YEAR FROM date[1])=?";
                nb++;
                blocAn = nb;
            }
            if (criteres.getMois() != 0) {
                if (nb > 0) {
                    requete += " AND";
                }
                requete += " EXTRACT (MONTH FROM date[1])=? ";
                nb++;
                blocM = nb;
            }
            if (criteres.getNumero() != 0) {
                if (nb > 0) {
                    requete += " AND";
                }
                requete += " numero = ?";
                nb++;
                blocN = nb;
            }

            pstmt = connexion.prepareStatement(requete);
            if (blocnO > 0) {
                pstmt.setString(blocnO, criteres.getNomIntervenant());
            }
            if (blocS > 0) {
                pstmt.setInt(blocS, criteres.getSemaine());
            }
            if (blocSess > 0) {
                pstmt.setString(blocSess, criteres.getSession());
            }
            if (blocSite > 0) {
                pstmt.setByte(blocSite, criteres.getSite());
            }
            if (blocAn > 0) {
                pstmt.setInt(blocAn, criteres.getAnnee());
            }
            if (blocM > 0) {
                pstmt.setInt(blocM, criteres.getMois());
            }
            if (blocN > 0) {
                pstmt.setInt(blocN, criteres.getNumero());
            }

            //fin modif lucile
            resultats = pstmt.executeQuery();

            while (resultats.next()) {
                ArrayList dates = new ArrayList(Arrays.asList(resultats.getArray(11)));

                String raisonsociale = null;
                String adresse = null;
                String ville = null;
                String codepostal = null;
                String signataire = null;
                requete = "SELECT * FROM prestataires WHERE id_prestataire = ?";
                pstmt = connexion.prepareStatement(requete);
                pstmt.setInt(1, resultats.getInt(14));
                ResultSet rs1 = pstmt.executeQuery();
                if (rs1.next()) {
                    raisonsociale = rs1.getString(2);
                    adresse = rs1.getString(3);
                    codepostal = rs1.getString(4);
                    ville = rs1.getString(5);
                    signataire = rs1.getString(6);

                }
                String nomintervenant = null;
                String tel = null;
                String email = null;
                requete = "SELECT * FROM intervenants WHERE id_intervenant = ?";
                pstmt = connexion.prepareStatement(requete);
                pstmt.setInt(1, resultats.getInt(15));
                ResultSet rs2 = pstmt.executeQuery();
                if (rs2.next()) {
                    nomintervenant = rs2.getString(2);
                    tel = rs2.getString(3);
                    email = rs2.getString(4);
                }
                BonDeCommande bon = new BonDeCommande(resultats.getInt(1), resultats.getInt(2), resultats.getByte(3),
                        raisonsociale, adresse, codepostal, ville, signataire, nomintervenant, tel, email, dates, resultats.getString(5), 
                        resultats.getString(6), resultats.getBigDecimal(7), resultats.getByte(8), resultats.getString(9), resultats.getInt(10), 
                        resultats.getInt(11), resultats.getByte(12), resultats.getString(13));
                resultatsRecherche.add(bon);
                System.out.println(bon.getId());
            }
        } catch (ClassNotFoundException | SQLException e) {
            this.notifyObservers(e);
        }
        this.notifyObservers(resultatsRecherche);

    }

    public void modifierBdc(BonDeCommande bdc) {

        this.setChanged();
        Connection connexion;
        int idPrestataire;
        int idIntervenant;
        PreparedStatement pstmt;

        try {
            Class.forName("org.postgresql.Driver");
            connexion = DriverManager.getConnection("jdbc:postgresql://10.0.1.65:5432/gbdc", "bdcgen", "mdp");
            connexion.setAutoCommit(false);
            
            try {
            idPrestataire = chercherPrestataire(bdc, connexion);
            if (idPrestataire == -1) {
                creerPrestataire(bdc, connexion);
            }
            idIntervenant = chercherIntervenant(bdc, connexion);
            if (idIntervenant == -1) {
                creerIntervenant(bdc, connexion);
            }
            Timestamp[] tabDates = new Timestamp[bdc.getDates().size()];
            int nb = 0;
            for (DateTime date : bdc.getDates()) {
                tabDates[nb] = new Timestamp(date.getMillis());
                nb++;
            }
            String saisie = "UPDATE bon_de_commande SET site = ?, date = ? ,designation = ?, session = ?, "
                    + "prixunitaire = ?, horaires=?, informations = ?, delaiminimum = ?, delaimaximum = ?, "
                    + "moyenreglement = ?, observations = ?, id_prestataire = ?,id_intervenant = ? WHERE id_bdc = ?";
            Array array = connexion.createArrayOf("timestamp", tabDates);

            pstmt = connexion.prepareStatement(saisie);

            pstmt.setByte(1, bdc.getSite());
            pstmt.setArray(2, array);
            pstmt.setString(3, bdc.getDesignation());
            pstmt.setString(4, bdc.getSession());
            pstmt.setBigDecimal(5, bdc.getPrixUnitaire());
            pstmt.setByte(6, bdc.getHoraires());
            pstmt.setString(7, bdc.getInformations());
            pstmt.setInt(8, bdc.getDelaiMinimum());
            pstmt.setInt(9, bdc.getDelaiMaximum());
            pstmt.setByte(10, bdc.getMoyenReglement());
            pstmt.setString(11, bdc.getObservations());
            pstmt.setInt(12, idPrestataire);
            pstmt.setInt(13, idIntervenant);
            pstmt.setInt(14, bdc.getId());
            pstmt.executeUpdate();
            
            connexion.commit();
            } catch (Exception e) {
                connexion.rollback();
            this.notifyObservers(e);
            }
        } catch (SQLException | ClassNotFoundException e) {
            this.notifyObservers(e);
        }
        ArrayList bdcModifie = new ArrayList<BonDeCommande>();
        bdcModifie.add(bdc);
        this.notifyObservers(bdcModifie);
    }

    public void genererOdt(BonDeCommande bdcOdt) {

        this.setChanged();
        File templateFile = new File("fichiers/template.odt");
        File outFile = new File("Bon de Commande.odt");
        try {
            JavaScriptFileTemplate template = new JavaScriptFileTemplate(templateFile);

            template.setField("annee", bdcOdt.getDates().get(1).getYear());
            template.setField("mois", bdcOdt.getDates().get(1).getMonthOfYear());
            template.setField("numero", bdcOdt.getNumero());
            switch (bdcOdt.getSite()) {
                case 1:
                    template.setField("mail", "angers");
                    template.setField("siteAngers", "X");
                    template.setField("lieu", "1 Rue Victor Hugo\nImmeuble Agora\n44400 REZE");
                    break;
                case 2:
                    template.setField("mail", "nantes");
                    template.setField("siteNantes", "X");
                    template.setField("lieu", "12 Rue Philippe La Peyrouse\nCentre Soulez Lariviere\n49000 Angers");
                    break;
                case 3:
                    template.setField("mail", "rennes");
                    template.setField("siteRennes", "X");
                    template.setField("lieu", "Rue Pierre de Maupertuis\nCampus de Ker Lann\nImmeuble Alliance\n35170 Bruz");
                    break;
            }
            template.setField("raisonSociale", bdcOdt.getRaisonSociale());
            template.setField("adresse", bdcOdt.getAdresse());
            template.setField("codePostal", bdcOdt.getCodePostal());
            template.setField("ville", bdcOdt.getVille());
            template.setField("nomSignataire", bdcOdt.getNomSignataire());
            template.setField("nomIntervenant", bdcOdt.getNomIntervenant());
            template.setField("telephone", bdcOdt.getTelIntervenant());
            template.setField("mailIntervenant", bdcOdt.getEmailIntervenant());

            List<Map<String, String>> listeLignes = new ArrayList<Map<String, String>>();

            Collections.sort(bdcOdt.getDates());
            int semaine = bdcOdt.getDates().get(1).getWeekOfWeekyear();
            int semaineMaxi = bdcOdt.getDates().get(1).getWeekOfWeekyear();
            String stringDates = "";
            double nb;
            BigDecimal total;
            BigDecimal montant = BigDecimal.valueOf(0);
            do {
                nb = 0;
                for (DateTime date : bdcOdt.getDates()) {
                    if (date.getWeekOfWeekyear() == semaine) {
                        stringDates += date.getDayOfWeek() + " " + date.getDayOfMonth() + " " + date.getMonthOfYear();

                        switch (date.getHourOfDay()) {
                            case BonEnSaisie.MATINEE:
                                stringDates += " matin, ";
                                nb += 0.5;
                                break;
                            case BonEnSaisie.APRES_MIDI:
                                stringDates += " après-midi, ";
                                nb += 0.5;
                                break;
                            default:
                                stringDates += ", ";
                                nb++;
                        }
                    }
                }
                total = bdcOdt.getPrixUnitaire().multiply(BigDecimal.valueOf(nb));
                montant = montant.add(total);
                listeLignes.add(this.creerLigne(String.valueOf(semaine), stringDates, bdcOdt.getDesignation(),
                        bdcOdt.getSession(), String.valueOf(nb), String.valueOf(bdcOdt.getPrixUnitaire()), String.valueOf(total)));
                semaine++;
            } while (semaine < semaineMaxi);
            template.setField("commande", listeLignes);

            template.setField("prixglobal", montant+"€");

            switch (bdcOdt.getHoraires()) {
                case 1:
                    template.setField("horaire1", "X");
                    break;
                case 2:
                    template.setField("horaire2", "X");
                    break;
            }
            template.setField("delaiMini", bdcOdt.getDelaiMinimum());
            template.setField("delaiMaxi", bdcOdt.getDelaiMaximum());
            template.setField("observations", bdcOdt.getObservations());
            switch (bdcOdt.getMoyenReglement()) {
                case 1:
                    template.setField("reglement1", "X");
                    break;
                case 2:
                    template.setField("reglement2", "X");
                    break;
            }
            template.setField("signatureJour", DateTime.now().getDayOfWeek());
            template.setField("signatureDate", DateTime.now().getDayOfMonth());
            template.setField("signatureMois", DateTime.now().getMonthOfYear());
            template.setField("signatureAnnee", DateTime.now().getYear());

            template.createDocument().saveToPackageAs(outFile);

        } catch (IOException | TemplateException | JDOMException e) {
            this.notifyObservers(e);
        }
        this.notifyObservers(outFile);
    }

    private int chercherPrestataire (BonEnSaisie bonEnSaisie, Connection connexion) throws Exception {

        int idPrestataire = -1;
        PreparedStatement pstmt;
        ResultSet resultats;
        int nb = 0;
        int blocRS = 0;
        int blocAd = 0;
        int blocCp = 0;
        int blocV = 0;
        int blocNs = 0;

        try {
            String requete = "SELECT id_prestataire, raisonsociale, adresse, codepostal, ville, signataire FROM prestataires WHERE";
            if (bonEnSaisie.getRaisonSociale() != null) {
                requete += " raisonsociale = ?";
                nb++;
                blocRS = nb;
            }
            if (bonEnSaisie.getAdresse() != null) {
                if (nb > 0) {
                    requete += " AND";
                }
                requete += " adresse = ?";
                nb++;
                blocAd = nb;
            }
            if (bonEnSaisie.getCodePostal() != null) {
                if (nb > 0) {
                    requete += " AND";
                }
                requete += " codepostal = ?";
                nb++;
                blocCp = nb;
            }
            if (bonEnSaisie.getVille() != null) {
                if (nb > 0) {
                    requete += " AND";
                }
                requete += " ville = ?";
                nb++;
                blocV = nb;
            }
            if (bonEnSaisie.getNomSignataire() != null) {
                if (nb > 0) {

                    requete += " AND";
                }
                requete += " signataire = ?";
                nb++;
                blocNs = nb;
            }
            pstmt = connexion.prepareStatement(requete);
            if (blocRS > 0) {
                pstmt.setString(blocRS, bonEnSaisie.getRaisonSociale());
            }
            if (blocAd > 0) {
                pstmt.setString(blocAd, bonEnSaisie.getAdresse());

            }
            if (blocCp > 0) {
                pstmt.setString(blocCp, bonEnSaisie.getCodePostal());
            }
            if (blocV > 0) {

                pstmt.setString(blocV, bonEnSaisie.getVille());
            }

            if (blocNs > 0) {

                pstmt.setString(blocNs, bonEnSaisie.getNomSignataire());
            }
            resultats = pstmt.executeQuery();
            if (resultats.next()) {
                idPrestataire = resultats.getInt(1);
            }
        } catch (SQLException e) {
            throw new Exception(e.getMessage());
        }
        return (idPrestataire);
    }

    private int creerPrestataire(BonEnSaisie bonEnSaisie, Connection connexion) throws Exception {
        int idPrestataire = 1;
        PreparedStatement pstmt;
        ResultSet resultats;
        try {

            String requete = "SELECT * FROM prestataires";
            Statement stmt = connexion.createStatement();
            resultats = stmt.executeQuery(requete);
            while (resultats.next()) {

                if (resultats.getInt(1) > idPrestataire) {

                    idPrestataire = resultats.getInt(1);

                }
            }

            String saisie = "INSERT INTO prestataires VALUES (?, ?, ?,?,?,?)";
            idPrestataire++;
            pstmt = connexion.prepareStatement(saisie);
            pstmt.setInt(1, idPrestataire);
            pstmt.setString(2, bonEnSaisie.getRaisonSociale());
            pstmt.setString(3, bonEnSaisie.getAdresse());
            pstmt.setString(4, bonEnSaisie.getCodePostal());
            pstmt.setString(5, bonEnSaisie.getVille());
            pstmt.setString(6, bonEnSaisie.getNomSignataire());

            pstmt.executeUpdate();

        } catch (SQLException e) {
            throw new Exception(e.getMessage());
        }
        return (idPrestataire);
    }

    private int chercherIntervenant(BonEnSaisie bonEnSaisie, Connection connexion) throws Exception {

        int idIntervenant = -1;
        PreparedStatement pstmt;
        ResultSet resultats;
        int nb = 0;
        int blocN = 0;
        int blocTel = 0;
        int blocEm = 0;

        try {
            String requete = "SELECT id_intervenant, nom, telephone, email FROM intervenants WHERE";
            if (bonEnSaisie.getNomIntervenant() != null) {
                requete += " nom = ?";
                nb++;
                blocN = nb;
            }
            if (bonEnSaisie.getTelIntervenant() != null) {
                if (nb > 0) {

                    requete += " AND";
                }
                requete += " telephone = ?";
                nb++;
                blocTel = nb;
            }
            if (bonEnSaisie.getEmailIntervenant() != null) {
                if (nb > 0) {

                    requete += " AND";
                }
                requete += " email = ?";
                nb++;
                blocEm = nb;
            }
            pstmt = connexion.prepareStatement(requete);
            if (blocN > 0) {
                pstmt.setString(blocN, bonEnSaisie.getNomIntervenant());
            }
            if (blocTel > 0) {
                pstmt.setString(blocTel, bonEnSaisie.getTelIntervenant());
            }
            if (blocEm > 0) {
                pstmt.setString(blocEm, bonEnSaisie.getEmailIntervenant());
            }
            resultats = pstmt.executeQuery();
            if (resultats.next()) {
                idIntervenant = resultats.getInt(1);
            }
        } catch (SQLException e) {
            throw new Exception(e.getMessage());
        }
        return (idIntervenant);
    }

    private int creerIntervenant(BonEnSaisie bonEnSaisie, Connection connexion) throws Exception {
        int idIntervenant = 0;
        PreparedStatement pstmt;
        ResultSet resultats;
        try {
            String requete = "SELECT * FROM intervenants";
            Statement stmt = connexion.createStatement();
            resultats = stmt.executeQuery(requete);
            while (resultats.next()) {

                if (resultats.getInt(1) > idIntervenant) {

                    idIntervenant = resultats.getInt(1);
                }
            }
            String saisie = "INSERT INTO intervenants VALUES (?,?,?,?)";
            idIntervenant++;
            pstmt = connexion.prepareStatement(saisie);
            pstmt.setInt(1, idIntervenant);
            pstmt.setString(2, bonEnSaisie.getNomIntervenant());
            pstmt.setString(3, bonEnSaisie.getTelIntervenant());
            pstmt.setString(4, bonEnSaisie.getEmailIntervenant());

            pstmt.executeUpdate();

        } catch (SQLException e) {
            throw new Exception(e.getMessage());
        }
        return (idIntervenant);
    }

    private CriteresRecherche creerBonDeCommande (BonEnSaisie bonEnSaisie, int idPrestataire, int idIntervenant, Connection connexion) throws Exception {

        int idBonDeCommande = 0;
        PreparedStatement pstmt;
        int an = 0;
        ResultSet resultats;
        int numero = 1;
        int nb = 0;
        CriteresRecherche retour;

        for (DateTime date : bonEnSaisie.getDates()) {
            if (an > date.getYear()) {
                an = date.getYear();
            }
        }
        try {
            String requete = "SELECT * FROM bon_de_commande";
            Statement stmt = connexion.createStatement();
            resultats = stmt.executeQuery(requete);
            while (resultats.next()) {
                if (resultats.getInt(1) > idBonDeCommande) {
                    idBonDeCommande = resultats.getInt(1);
                }
            }
            idBonDeCommande++;
            
            requete = "SELECT numero FROM bon_de_commande WHERE EXTRACT(YEAR FROM date[1])= ?";
            pstmt = connexion.prepareStatement(requete);
            pstmt.setInt(1, an);
            resultats = pstmt.executeQuery();
            while (resultats.next()) {
                if (numero < resultats.getInt(1)) {
                    numero = resultats.getInt(1);
                }
            }
            Timestamp[] tabDates = new Timestamp[bonEnSaisie.getDates().size()];
            for (DateTime date : bonEnSaisie.getDates()) {
                tabDates[nb] = new Timestamp(date.getMillis());
                nb++;
            }
            String saisie = "INSERT INTO bon_de_commande VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
            Array array = connexion.createArrayOf("timestamp", tabDates);
            pstmt = connexion.prepareStatement(saisie);
            
            pstmt.setInt(1, idBonDeCommande);
            pstmt.setInt(2, numero);
            pstmt.setByte(3, bonEnSaisie.getSite());
            pstmt.setArray(4, array);
            pstmt.setString(5, bonEnSaisie.getDesignation());
            pstmt.setString(6, bonEnSaisie.getSession());
            pstmt.setBigDecimal(7, bonEnSaisie.getPrixUnitaire());
            pstmt.setByte(8, bonEnSaisie.getHoraires());
            pstmt.setString(9, bonEnSaisie.getInformations());
            pstmt.setInt(10, bonEnSaisie.getDelaiMinimum());
            pstmt.setInt(11, bonEnSaisie.getDelaiMaximum());
            pstmt.setByte(12, bonEnSaisie.getMoyenReglement());
            pstmt.setString(13, bonEnSaisie.getObservations());
            pstmt.setInt(14, idPrestataire);
            pstmt.setInt(15, idIntervenant);

            pstmt.executeUpdate();

        } catch (SQLException e) {
            throw new Exception(e.getMessage());
        }
        retour = new CriteresRecherche(null, 0, null, (byte) 0, an, 0, numero);
        return (retour);
    }

    private Map creerLigne(String semaine, String dates, String designation, String session, String quantite, String prixUnitaire, String prixTotal) {

        Map<String, String> hm = new HashMap<>();
        hm.put("semaine", semaine);
        hm.put("dates", dates);
        hm.put("designation", designation);
        hm.put("session", session);
        hm.put("quantite", quantite);
        hm.put("prixUnitaire", prixUnitaire+"€");
        hm.put("prixTotal", prixTotal+"€");
        return (hm);
    }

}
